Skip to content

feat: implement dashboard infrastructure with new charts, sidebar com…#79

Merged
Vamsi-o merged 1 commit into
mainfrom
Dashboard-ui
May 2, 2026
Merged

feat: implement dashboard infrastructure with new charts, sidebar com…#79
Vamsi-o merged 1 commit into
mainfrom
Dashboard-ui

Conversation

@TejaBudumuru3

@TejaBudumuru3 TejaBudumuru3 commented May 2, 2026

Copy link
Copy Markdown
Contributor

…ponents, and custom color theme styling

  • charts, Integrations cards and integartion new route and dashboard backend logic needs to be work

Summary by CodeRabbit

Release Notes

  • New Features

    • Added Dashboard page with workflow overview metrics, execution health gauge, and trend visualization with selectable time ranges (7/30/90 days)
    • Integrated Integration status display and Recent Workflows view on dashboard
    • Added Dashboard navigation to sidebar
  • UI Updates

    • Updated app styling with new green accent color theme across dashboard, login, register, and input components

…ponents, and custom color theme styling

- charts, Integrations cards and integartion new route and dashboard backend logic needs to be work
@TejaBudumuru3 TejaBudumuru3 requested a review from Vamsi-o as a code owner May 2, 2026 14:36
@coderabbitai

coderabbitai Bot commented May 2, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

A comprehensive dashboard feature is introduced with backend API endpoints for overview metrics and execution trends, complemented by multiple client-side React components (stat cards, charts, gauges, integrations, recent workflows) orchestrated on a tabbed dashboard page. Supporting changes include an enhanced sidebar, refactored workflows page, new TypeScript types, Zod validation schemas, and unified green/dark theme styling across login, register, and input components.

Changes

Dashboard Feature Implementation

Layer / File(s) Summary
Type Definitions & Validation
apps/web/app/types/dashboard.types.ts, packages/common/src/index.ts
Introduces DashboardTab, DashboardRange, and data shapes for integrations, recent workflows, overview metrics, and execution trends. Zod schemas validate range selection and response structures.
Backend API Endpoints
apps/http-backend/src/routes/userRoutes/userRoutes.ts
Two authenticated routes: GET /dashboard/overview computes workflow counts, execution stats, quotas, integration flags, and recent workflows; GET /dashboard/executions/trend aggregates per-day execution totals (completed/failed/in-flight) for selected range (7d/30d/90d), excluding test runs.
Frontend API Integration
apps/web/app/lib/api.ts
Adds api.dashboard.getOverview() and api.dashboard.getExecutionTrend(range) methods to fetch backend data with credentials.
Dashboard UI Components
apps/web/app/components/dashboard/DashboardSidebar.tsx, DashboardStatCard.tsx, ExecutionHealthGauge.tsx, IntegrationStatus.tsx, RecentWorkflows.tsx, WorkflowActivityChart.tsx
Sidebar with primary/secondary navigation tabs and user footer; stat card with optional trend indicator; pie-chart health gauge; integration status bar visualization; recent workflows table; execution trend area chart with range selector.
Dashboard Page & Orchestration
apps/web/app/dashboard/page.tsx
Client-side page with tabbed navigation driven by URL query parameter, manages independent overview/trend data fetching with loading/error states, renders stat cards, activity chart, health gauge, integration status, and recent workflows; conditionally switches between main dashboard and secondary tabs (automations, placeholder panels).
Workflows Refactoring & Integration
apps/web/app/components/workflows/WorkflowListView.tsx, apps/web/app/workflows/page.tsx
Extracted WorkflowListView reusable component handling workflow data fetching, list rendering, and config preview; refactored workflows page to delegate rendering to WorkflowListView.
Sidebar & Navigation Updates
apps/web/app/components/ui/app-sidebar.tsx
Dashboard menu item now includes onClick handler to navigate to /dashboard via router.push().
Theme & Global Styling
packages/ui/src/styles/globals.css
Added Google Fonts Inter import, replaced .dark CSS variable palette from oklch() to explicit hex values (dark green theme), updated body font-family, added dark-theme scrollbar styling, and introduced dashboard animations (fadeInUp, shimmer, pulse-glow).
UI Component & Page Styling
apps/web/app/components/ExecutionHistoryFooter.tsx, apps/web/app/components/ui/Inputbox.tsx, apps/web/app/login/page.tsx, apps/web/app/register/page.tsx
Updated color theme from light/gray to dark/green across status badges, footer buttons, input containers, and login/register card styling; replaced hard-coded colors with new palette while preserving functional behavior.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • Vamsi-o

🐰 With whiskers twitched and nose held high,
A dashboard bright now graces the sky!
Charts that trend and gauges that glow,
Green themes dance where data once flowed. ✨📊
Workflows list and health on display—
Hop along, let's work all the day! 🌟

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: implementing dashboard infrastructure with charts, sidebar components, and color theme styling, which aligns with the majority of changes across multiple dashboard-related files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch Dashboard-ui

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 60 minutes.

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

🧹 Nitpick comments (5)
apps/http-backend/src/routes/userRoutes/userRoutes.ts (1)

391-392: 💤 Low value

req.query.range can be a string[] in Express when the param is repeated.

If a caller sends ?range=7d&range=30d, Express parses req.query.range as ["7d", "30d"]. The ?? fallback only catches undefined, so an array would reach DashboardRangeSchema.safeParse and correctly fail with a 400. This is safe, but if you want to be explicit:

-const rangeInput = req.query.range ?? "7d";
+const rangeInput = typeof req.query.range === "string" ? req.query.range : "7d";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/http-backend/src/routes/userRoutes/userRoutes.ts` around lines 391 -
392, req.query.range may be a string[] when the param is repeated, so ensure
rangeInput is always a string before passing to DashboardRangeSchema.safeParse;
update the code around rangeInput/parsedRange to coerce arrays to a single value
(e.g., Array.isArray(req.query.range) ? req.query.range[0] : req.query.range)
and then apply the existing "?? '7d'" fallback, keeping the subsequent use of
DashboardRangeSchema.safeParse(rangeInput) unchanged.
apps/web/app/components/ui/Inputbox.tsx (1)

28-67: 💤 Low value

Theme update looks consistent with the rest of the PR.

One minor note: the animate-pulse class on the error span (Line 67) triggers a continuous animation. Users with vestibular disorders may be affected. Consider applying it conditionally (e.g., only on initial render) or using motion-safe:animate-pulse to respect the OS prefers-reduced-motion setting.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/app/components/ui/Inputbox.tsx` around lines 28 - 67, The error span
in the Inputbox component currently uses "animate-pulse" which forces continuous
motion; change this to respect users' reduced-motion preference by replacing the
class with "motion-safe:animate-pulse" on the error <span> (the span rendered
when error is truthy), or alternatively add a short-lived mounted state (e.g.,
in the Inputbox component) to apply "animate-pulse" only on initial render and
then remove it — update the span's className logic to reference that state or
the motion-safe utility while keeping the rest of the error rendering intact.
apps/web/app/components/dashboard/WorkflowActivityChart.tsx (1)

98-105: ⚡ Quick win

Hardcoded SVG gradient IDs will conflict if this component is ever rendered more than once per page.

id="greenGradient" and id="grayGradient" are global within the SVG/DOM. If a second WorkflowActivityChart instance is mounted (e.g., in a comparison view, a modal, or a unit test), both url(#greenGradient) references point to the first gradient definition, breaking the fill colors of all subsequent instances.

Use a per-instance unique suffix (e.g., via useId from React 18+):

♻️ Proposed fix
+import { useMemo, useId } from "react";
 ...
 
+  const uid = useId();
+  const greenId = `greenGradient-${uid}`;
+  const grayId = `grayGradient-${uid}`;
 
   ...
 
-                <linearGradient id="greenGradient" x1="0" y1="0" x2="0" y2="1">
+                <linearGradient id={greenId} x1="0" y1="0" x2="0" y2="1">
                   ...
-                <linearGradient id="grayGradient" x1="0" y1="0" x2="0" y2="1">
+                <linearGradient id={grayId} x1="0" y1="0" x2="0" y2="1">
                   ...
-              <Area ... fill="url(`#greenGradient`)" />
-              <Area ... fill="url(`#grayGradient`)" />
+              <Area ... fill={`url(#${greenId})`} />
+              <Area ... fill={`url(#${grayId})`} />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/app/components/dashboard/WorkflowActivityChart.tsx` around lines 98
- 105, The SVG gradient IDs in WorkflowActivityChart (greenGradient and
grayGradient) are hardcoded and will collide across multiple mounted instances;
update the component to generate per-instance unique IDs (e.g., via React's
useId or another unique suffix) and replace the literal id attributes and any
corresponding url(`#greenGradient`)/url(`#grayGradient`) references with the
generated ids so each chart instance references its own gradients.
apps/web/app/lib/api.ts (1)

115-115: ⚡ Quick win

Import and reuse DashboardRange instead of duplicating the inline literal type.

The parameter type "7d" | "30d" | "90d" is a verbatim copy of DashboardRange defined in dashboard.types.ts. This creates a silent divergence risk if the range options ever change.

♻️ Proposed refactor
+import { DashboardRange } from "@/app/types/dashboard.types";
 
 ...
 
-    getExecutionTrend: async (range: "7d" | "30d" | "90d" = "7d") => {
+    getExecutionTrend: async (range: DashboardRange = "7d") => {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/app/lib/api.ts` at line 115, Replace the inline literal type on the
getExecutionTrend parameter with the shared DashboardRange type: import
DashboardRange from the module where it's declared (dashboard.types.ts) and
change the signature from getExecutionTrend: async (range: "7d" | "30d" | "90d"
= "7d") => to getExecutionTrend: async (range: DashboardRange = "7d") =>; ensure
the import name matches the exported symbol (DashboardRange) and update any
related usages or tests to use the unified type.
apps/web/app/dashboard/page.tsx (1)

72-97: ⚡ Quick win

Inflight fetch requests are not cancelled on component unmount.

fetchOverview and fetchTrend have no cleanup path. If the user navigates away while either fetch is still in-flight, the setState calls inside finally/catch will execute on an unmounted component (React 18 no-ops them, but the requests keep running and the associated closures are retained until settlement). The fix is to thread an AbortSignal through each fetch and return a cleanup function from each useEffect:

♻️ Proposed fix
-  const fetchOverview = async () => {
+  const fetchOverview = async (signal?: AbortSignal) => {
     setOverviewLoading(true);
     setOverviewError(null);
     try {
-      setOverview(await api.dashboard.getOverview());
+      setOverview(await api.dashboard.getOverview(signal));
     } catch (e: any) {
+      if (e?.name === "AbortError") return;
       setOverviewError(e?.message || "Failed to load overview");
     } finally {
       setOverviewLoading(false);
     }
   };

-  const fetchTrend = async (range: DashboardRange) => {
+  const fetchTrend = async (range: DashboardRange, signal?: AbortSignal) => {
     setTrendLoading(true);
     setTrendError(null);
     try {
-      setTrend(await api.dashboard.getExecutionTrend(range));
+      setTrend(await api.dashboard.getExecutionTrend(range, signal));
     } catch (e: any) {
+      if (e?.name === "AbortError") return;
       setTrendError(e?.message || "Failed to load trend");
     } finally {
       setTrendLoading(false);
     }
   };

-  useEffect(() => { fetchOverview(); }, []);
-  useEffect(() => { fetchTrend(selectedRange); }, [selectedRange]);
+  useEffect(() => {
+    const controller = new AbortController();
+    fetchOverview(controller.signal);
+    return () => controller.abort();
+  }, []);
+
+  useEffect(() => {
+    const controller = new AbortController();
+    fetchTrend(selectedRange, controller.signal);
+    return () => controller.abort();
+  }, [selectedRange]);

api.dashboard.getOverview and api.dashboard.getExecutionTrend would need to forward the signal to their underlying fetch calls.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/app/dashboard/page.tsx` around lines 72 - 97, fetchOverview and
fetchTrend lack cancellation which lets in-flight requests continue after
unmount; update both functions to accept and forward an AbortSignal to
api.dashboard.getOverview and api.dashboard.getExecutionTrend (ensure those
functions pass the signal to their underlying fetch), create an AbortController
inside each useEffect, call the respective fetch with controller.signal, and
return a cleanup from each useEffect that calls controller.abort(); also handle
aborted errors in the catch paths of fetchOverview/fetchTrend to avoid treating
abort as a real error (check error.name === "AbortError" or similar) while
keeping setState calls only for non-aborted results.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/http-backend/src/routes/userRoutes/userRoutes.ts`:
- Around line 303-318: The current prismaClient.workflowExecution.findMany call
loads all executions into memory and then filters with isTestingExecution,
causing unbounded growth; add an isTest boolean column to the workflowExecution
model and change the aggregate logic (e.g., use
prismaClient.workflowExecution.count with where: { workflow: { userId }, isTest:
false } and status filters) so counts are computed server-side, or as a
short-term mitigation add a take cap and appropriate ordering on
prismaClient.workflowExecution.findMany and document the approximation; update
any other usages (e.g., the trend endpoint's findMany) similarly to avoid
unbounded result sets.

In `@apps/web/app/components/dashboard/DashboardSidebar.tsx`:
- Line 155: The hardcoded personal email in DashboardSidebar.tsx (the onClick
handler on the upgrade button) should be replaced with a product/team address
stored as a config constant or environment variable; update the onClick in the
button (the anonymous function that sets window.location.href) to reference that
constant (e.g., UPGRADE_CONTACT_EMAIL or config.upgradeEmail) and ensure the
mailto subject remains, then load that constant from a safe config/env module so
the personal address is removed from the client bundle.

In `@apps/web/app/components/dashboard/DashboardStatCard.tsx`:
- Line 7: The type annotation uses the React namespace ("icon: React.ReactNode")
but React isn't imported, so add an import to fix the TS error; either import
the namespace (import React from 'react') or import the type directly (import
type { ReactNode } from 'react' and change the annotation to "icon: ReactNode")
— update the DashboardStatCard component's prop type where "icon:
React.ReactNode" appears to use the imported symbol.

In `@apps/web/app/components/dashboard/ExecutionHealthGauge.tsx`:
- Around line 30-32: The icon-only button in ExecutionHealthGauge.tsx using the
<MoreHorizontal /> icon is missing an accessible label; update the <button>
element to include an aria-label (or aria-labelledby) with a concise description
such as "More options" and/or add visually hidden text inside the button for
screen readers so assistive tech announces its purpose; ensure the change is
applied to the button element that currently wraps <MoreHorizontal /> and keep
existing classes and type attribute intact.
- Around line 17-21: The gaugeData construction in ExecutionHealthGauge
currently uses a fallback of failedRate || 0.5 which shows a misleading red arc
when failedRate is 0; change gaugeData to use an explicit check for zero and
either omit the Failed slice entirely or use a very small epsilon (e.g., 0.001)
purely for visual spacing and conditionally hide its legend/label so the center
percentage remains accurate; update the array creation in the gaugeData useMemo
and any legend-rendering logic to skip / hide the failed entry when failedRate
=== 0.

In `@apps/web/app/components/dashboard/IntegrationStatus.tsx`:
- Around line 14-17: The iconMap entry for the googleSheets integration in
IntegrationStatus.tsx is mislabeled; update the iconMap Record (key:
googleSheets) to change its label from "Google Drive" to "Google Sheets" so it
matches the backend and renders correctly in the UI (locate the iconMap constant
near the top of the component).

In `@apps/web/app/components/dashboard/RecentWorkflows.tsx`:
- Line 81: The "Duration" column header in RecentWorkflows.tsx is misleading
because the cell value comes from getTimeDuration(wf.createdAt) (relative age),
so change the header text from "Duration" to "Age" (or "Created") and update any
related accessibility labels/titles/tooltip strings for that <th> element to
match (e.g., the <th> that currently contains "Duration" and any aria-labels or
tests that reference it); ensure the display and semantics now align with
getTimeDuration(wf.createdAt).

In `@apps/web/app/components/dashboard/WorkflowActivityChart.tsx`:
- Line 62: The static "CRM" label in WorkflowActivityChart.tsx is a stale
placeholder and should be removed or replaced with a meaningful label; locate
the span rendering the text (the <span className="text-xs
text-[`#5a6350`]">CRM</span> near the peak completed count) and either delete that
span or change its text to a contextual string like "Peak" (or another suitable
term), keeping the existing styling and layout so only the label content is
updated.
- Line 37: The X-axis label generation in WorkflowActivityChart (the label
property in the data mapping) uses .charAt(0) which collapses distinct weekdays
into identical single letters; update the label expression to use the full
3-letter abbreviation returned by toLocaleDateString (e.g., new
Date(p.date).toLocaleDateString("en-US", { weekday: "short" })) instead of
slicing to a single character so Tuesday/Thursday and Saturday/Sunday remain
distinguishable.

In `@packages/ui/src/styles/globals.css`:
- Line 2: Replace the `@import` url(...) statement in globals.css with the string
notation required by Stylelint: change the current `@import`
url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap');
to the string form `@import`
'https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap';
so the import-notation rule is satisfied.
- Around line 148-171: Rename the keyframe from fadeInUp to kebab-case (e.g.,
fade-in-up) and update the animation reference in the .animate-fade-in-up
utility to use the new name (animation: fade-in-up 0.5s ease-out forwards);
ensure the `@keyframes` rule and any other places referencing fadeInUp are changed
to the new kebab-case identifier so it satisfies the keyframes-name-pattern
rule.
- Around line 126-128: The font-family declaration after the `@apply` rule in the
global CSS violates Stylelint: add a blank line before the font-family property
and remove the quotes around Inter (use Inter without quotes) so the declaration
reads an unquoted Inter followed by the existing fallbacks; update the rule near
the `@apply` bg-background text-foreground block (the font-family line) to satisfy
declaration-empty-line-before and font-family-name-quotes.

---

Nitpick comments:
In `@apps/http-backend/src/routes/userRoutes/userRoutes.ts`:
- Around line 391-392: req.query.range may be a string[] when the param is
repeated, so ensure rangeInput is always a string before passing to
DashboardRangeSchema.safeParse; update the code around rangeInput/parsedRange to
coerce arrays to a single value (e.g., Array.isArray(req.query.range) ?
req.query.range[0] : req.query.range) and then apply the existing "?? '7d'"
fallback, keeping the subsequent use of
DashboardRangeSchema.safeParse(rangeInput) unchanged.

In `@apps/web/app/components/dashboard/WorkflowActivityChart.tsx`:
- Around line 98-105: The SVG gradient IDs in WorkflowActivityChart
(greenGradient and grayGradient) are hardcoded and will collide across multiple
mounted instances; update the component to generate per-instance unique IDs
(e.g., via React's useId or another unique suffix) and replace the literal id
attributes and any corresponding url(`#greenGradient`)/url(`#grayGradient`)
references with the generated ids so each chart instance references its own
gradients.

In `@apps/web/app/components/ui/Inputbox.tsx`:
- Around line 28-67: The error span in the Inputbox component currently uses
"animate-pulse" which forces continuous motion; change this to respect users'
reduced-motion preference by replacing the class with
"motion-safe:animate-pulse" on the error <span> (the span rendered when error is
truthy), or alternatively add a short-lived mounted state (e.g., in the Inputbox
component) to apply "animate-pulse" only on initial render and then remove it —
update the span's className logic to reference that state or the motion-safe
utility while keeping the rest of the error rendering intact.

In `@apps/web/app/dashboard/page.tsx`:
- Around line 72-97: fetchOverview and fetchTrend lack cancellation which lets
in-flight requests continue after unmount; update both functions to accept and
forward an AbortSignal to api.dashboard.getOverview and
api.dashboard.getExecutionTrend (ensure those functions pass the signal to their
underlying fetch), create an AbortController inside each useEffect, call the
respective fetch with controller.signal, and return a cleanup from each
useEffect that calls controller.abort(); also handle aborted errors in the catch
paths of fetchOverview/fetchTrend to avoid treating abort as a real error (check
error.name === "AbortError" or similar) while keeping setState calls only for
non-aborted results.

In `@apps/web/app/lib/api.ts`:
- Line 115: Replace the inline literal type on the getExecutionTrend parameter
with the shared DashboardRange type: import DashboardRange from the module where
it's declared (dashboard.types.ts) and change the signature from
getExecutionTrend: async (range: "7d" | "30d" | "90d" = "7d") => to
getExecutionTrend: async (range: DashboardRange = "7d") =>; ensure the import
name matches the exported symbol (DashboardRange) and update any related usages
or tests to use the unified type.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a0dff55c-9a1b-40f6-8919-3878ce628cf8

📥 Commits

Reviewing files that changed from the base of the PR and between f4876fe and c64d813.

📒 Files selected for processing (19)
  • apps/http-backend/src/routes/userRoutes/userRoutes.ts
  • apps/web/app/components/ExecutionHistoryFooter.tsx
  • apps/web/app/components/dashboard/DashboardSidebar.tsx
  • apps/web/app/components/dashboard/DashboardStatCard.tsx
  • apps/web/app/components/dashboard/ExecutionHealthGauge.tsx
  • apps/web/app/components/dashboard/IntegrationStatus.tsx
  • apps/web/app/components/dashboard/RecentWorkflows.tsx
  • apps/web/app/components/dashboard/WorkflowActivityChart.tsx
  • apps/web/app/components/ui/Inputbox.tsx
  • apps/web/app/components/ui/app-sidebar.tsx
  • apps/web/app/components/workflows/WorkflowListView.tsx
  • apps/web/app/dashboard/page.tsx
  • apps/web/app/lib/api.ts
  • apps/web/app/login/page.tsx
  • apps/web/app/register/page.tsx
  • apps/web/app/types/dashboard.types.ts
  • apps/web/app/workflows/page.tsx
  • packages/common/src/index.ts
  • packages/ui/src/styles/globals.css

Comment on lines +303 to +318
prismaClient.workflowExecution.findMany({
where: {
workflow: {
userId,
},
},
select: {
status: true,
metadata: true,
},
}),
]);

const executions = executionRows.filter(
(execution) => !isTestingExecution(execution.metadata)
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Unbounded findMany loads all historical executions into memory to compute statistics.

workflowExecution.findMany has no take limit and fetches every execution record ever created for the user's workflows. This grows without bound over time. The in-memory isTestingExecution filter prevents a direct groupBy, but there are two practical paths forward:

Option A (recommended): Add an isTest boolean column to workflowExecution at the schema level. This allows the statistics to be computed with cheap server-side aggregates:

// After schema migration
const [executionCount, failedCount, completedCount] = await Promise.all([
  prismaClient.workflowExecution.count({
    where: { workflow: { userId }, isTest: false },
  }),
  prismaClient.workflowExecution.count({
    where: { workflow: { userId }, isTest: false, status: "Failed" },
  }),
  prismaClient.workflowExecution.count({
    where: { workflow: { userId }, isTest: false, status: "Completed" },
  }),
]);

Option B (quick mitigation): Apply a take cap until the schema is updated, and acknowledge the approximation:

 prismaClient.workflowExecution.findMany({
   where: { workflow: { userId } },
   select: { status: true, metadata: true },
+  take: 5000,      // temporary safety cap; replace with DB-side aggregation
+  orderBy: { startAt: "desc" },
 }),

The same concern applies to the trend endpoint's findMany at Line 407, though the date-range filter bounds its growth to the selected window.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/http-backend/src/routes/userRoutes/userRoutes.ts` around lines 303 -
318, The current prismaClient.workflowExecution.findMany call loads all
executions into memory and then filters with isTestingExecution, causing
unbounded growth; add an isTest boolean column to the workflowExecution model
and change the aggregate logic (e.g., use prismaClient.workflowExecution.count
with where: { workflow: { userId }, isTest: false } and status filters) so
counts are computed server-side, or as a short-term mitigation add a take cap
and appropriate ordering on prismaClient.workflowExecution.findMany and document
the approximation; update any other usages (e.g., the trend endpoint's findMany)
similarly to avoid unbounded result sets.

<span className="text-[11px] text-[#5a6350] truncate">{user.email || 'user@example.com'}</span>
</div>
</div>
<button onClick={() => { window.location.href = 'mailto:tejabudumuru3@gmail.com?subject=BuildFlow-Upgrade' }} className="mt-4 w-full py-2 rounded-lg bg-[#baf266]/10 text-[#baf266] text-xs font-semibold border border-[#baf266]/20 hover:bg-[#baf266]/20 transition-all group-data-[state=collapsed]:hidden">

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Personal email address hardcoded in client-side production code.

tejabudumuru3@gmail.com is shipped to every user in the JS bundle and points directly to a personal mailbox, which is both a PII exposure and an inappropriate upgrade flow mechanism. Replace with a product/team address and consider moving it to an environment variable or config constant.

🔧 Proposed fix
-<button onClick={() => { window.location.href = 'mailto:tejabudumuru3@gmail.com?subject=BuildFlow-Upgrade' }} ...>
+<button onClick={() => { window.location.href = `mailto:${process.env.NEXT_PUBLIC_SUPPORT_EMAIL ?? 'support@buildflow.io'}?subject=BuildFlow-Upgrade` }} ...>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<button onClick={() => { window.location.href = 'mailto:tejabudumuru3@gmail.com?subject=BuildFlow-Upgrade' }} className="mt-4 w-full py-2 rounded-lg bg-[#baf266]/10 text-[#baf266] text-xs font-semibold border border-[#baf266]/20 hover:bg-[#baf266]/20 transition-all group-data-[state=collapsed]:hidden">
<button onClick={() => { window.location.href = `mailto:${process.env.NEXT_PUBLIC_SUPPORT_EMAIL ?? 'support@buildflow.io'}?subject=BuildFlow-Upgrade` }} className="mt-4 w-full py-2 rounded-lg bg-[`#baf266`]/10 text-[`#baf266`] text-xs font-semibold border border-[`#baf266`]/20 hover:bg-[`#baf266`]/20 transition-all group-data-[state=collapsed]:hidden">
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/app/components/dashboard/DashboardSidebar.tsx` at line 155, The
hardcoded personal email in DashboardSidebar.tsx (the onClick handler on the
upgrade button) should be replaced with a product/team address stored as a
config constant or environment variable; update the onClick in the button (the
anonymous function that sets window.location.href) to reference that constant
(e.g., UPGRADE_CONTACT_EMAIL or config.upgradeEmail) and ensure the mailto
subject remains, then load that constant from a safe config/env module so the
personal address is removed from the client bundle.


interface StatCardProps {
border: boolean;
icon: React.ReactNode;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify: search for other files using React.ReactNode without a React import
rg --type=ts --type=tsx -n "React\.ReactNode" -l | xargs grep -L "import React"

Repository: Dev-Pross/BuildFlow

Length of output: 128


🏁 Script executed:

# Check if the file exists and read its content
cat -n apps/web/app/components/dashboard/DashboardStatCard.tsx

Repository: Dev-Pross/BuildFlow

Length of output: 1902


🏁 Script executed:

# Check TypeScript configuration
cat apps/web/tsconfig.json

Repository: Dev-Pross/BuildFlow

Length of output: 521


🏁 Script executed:

# Find the workspace typescript config
fd -type f -name "*.json" | xargs grep -l "typescript-config/nextjs" | head -5

Repository: Dev-Pross/BuildFlow

Length of output: 234


🏁 Script executed:

# Look for the actual nextjs.json config
find . -path "*/typescript-config/nextjs.json" -o -path "*/typescript-config/*nextjs*"

Repository: Dev-Pross/BuildFlow

Length of output: 104


🏁 Script executed:

# Check if React is re-exported or provided globally
rg -i "react" packages/*/tsconfig.json --type json

Repository: Dev-Pross/BuildFlow

Length of output: 153


🏁 Script executed:

# Read the workspace nextjs.json config
cat packages/typescript-config/nextjs.json

Repository: Dev-Pross/BuildFlow

Length of output: 359


🏁 Script executed:

# Read the base config
cat packages/typescript-config/base.json

Repository: Dev-Pross/BuildFlow

Length of output: 587


React.ReactNode used without importing React — TypeScript compilation error.

The React namespace is referenced in the type annotation on line 7, but React is never imported. The new JSX transform (React 17+) only removes the need for the JSX factory call; accessing React.ReactNode as a namespace still requires an explicit import in module-scope TypeScript files. With strict: true enabled in tsconfig, this will cause a compilation failure.

🔧 Proposed fix
 "use client";
 
+import type { ReactNode } from "react";
 import { TrendingUp, TrendingDown } from "lucide-react";
 
 interface StatCardProps {
   border: boolean;
-  icon: React.ReactNode;
+  icon: ReactNode;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
icon: React.ReactNode;
"use client";
import type { ReactNode } from "react";
import { TrendingUp, TrendingDown } from "lucide-react";
interface StatCardProps {
border: boolean;
icon: ReactNode;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/app/components/dashboard/DashboardStatCard.tsx` at line 7, The type
annotation uses the React namespace ("icon: React.ReactNode") but React isn't
imported, so add an import to fix the TS error; either import the namespace
(import React from 'react') or import the type directly (import type { ReactNode
} from 'react' and change the annotation to "icon: ReactNode") — update the
DashboardStatCard component's prop type where "icon: React.ReactNode" appears to
use the imported symbol.

Comment on lines +17 to +21
const gaugeData = useMemo(() => [
{ name: "Success", value: successRate, color: "#baf266" },
{ name: "Delayed", value: delayedRate, color: "#f59e0b" },
{ name: "Failed", value: failedRate || 0.5, color: "#f87171" },
], [successRate, failedRate, delayedRate]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

failedRate || 0.5 shows a misleading non-zero "Failed" arc when there are zero failures.

When failedRate is 0, the gauge renders a visible red slice anyway, giving users the false impression that executions have failed. If the intent is purely cosmetic (ensure the arc is never invisible), the phantom value should at minimum not affect the center percentage and ideally not appear in the chart itself. Prefer an explicit check:

🐛 Proposed fix
-{ name: "Failed", value: failedRate || 0.5, color: "#f87171" },
+{ name: "Failed", value: failedRate, color: "#f87171" },

If a visible arc is truly required when failedRate is 0 for aesthetic spacing, use a vanishingly small value and hide the legend entry conditionally — but surfacing 0.5% as a real data point is the more damaging choice.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const gaugeData = useMemo(() => [
{ name: "Success", value: successRate, color: "#baf266" },
{ name: "Delayed", value: delayedRate, color: "#f59e0b" },
{ name: "Failed", value: failedRate || 0.5, color: "#f87171" },
], [successRate, failedRate, delayedRate]);
const gaugeData = useMemo(() => [
{ name: "Success", value: successRate, color: "#baf266" },
{ name: "Delayed", value: delayedRate, color: "#f59e0b" },
{ name: "Failed", value: failedRate, color: "#f87171" },
], [successRate, failedRate, delayedRate]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/app/components/dashboard/ExecutionHealthGauge.tsx` around lines 17 -
21, The gaugeData construction in ExecutionHealthGauge currently uses a fallback
of failedRate || 0.5 which shows a misleading red arc when failedRate is 0;
change gaugeData to use an explicit check for zero and either omit the Failed
slice entirely or use a very small epsilon (e.g., 0.001) purely for visual
spacing and conditionally hide its legend/label so the center percentage remains
accurate; update the array creation in the gaugeData useMemo and any
legend-rendering logic to skip / hide the failed entry when failedRate === 0.

Comment on lines +30 to +32
<button className="p-1 rounded-md hover:bg-[#1a2118] text-[#5a6350] transition-colors" type="button">
<MoreHorizontal className="h-4 w-4" />
</button>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Icon-only button is missing an accessible label.

<MoreHorizontal /> provides no text for assistive technologies; the button is announced as an unnamed button to screen readers.

♿ Proposed fix
-<button className="p-1 rounded-md hover:bg-[`#1a2118`] text-[`#5a6350`] transition-colors" type="button">
+<button className="p-1 rounded-md hover:bg-[`#1a2118`] text-[`#5a6350`] transition-colors" type="button" aria-label="More options">
   <MoreHorizontal className="h-4 w-4" />
 </button>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<button className="p-1 rounded-md hover:bg-[#1a2118] text-[#5a6350] transition-colors" type="button">
<MoreHorizontal className="h-4 w-4" />
</button>
<button className="p-1 rounded-md hover:bg-[`#1a2118`] text-[`#5a6350`] transition-colors" type="button" aria-label="More options">
<MoreHorizontal className="h-4 w-4" />
</button>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/app/components/dashboard/ExecutionHealthGauge.tsx` around lines 30 -
32, The icon-only button in ExecutionHealthGauge.tsx using the <MoreHorizontal
/> icon is missing an accessible label; update the <button> element to include
an aria-label (or aria-labelledby) with a concise description such as "More
options" and/or add visually hidden text inside the button for screen readers so
assistive tech announces its purpose; ensure the change is applied to the button
element that currently wraps <MoreHorizontal /> and keep existing classes and
type attribute intact.

if (!trend?.points) return [];
return trend.points.map((p) => ({
...p,
label: new Date(p.date).toLocaleDateString("en-US", { weekday: "short" }).charAt(0),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Single-character weekday labels produce ambiguous X-axis tick collisions.

charAt(0) produces "T" for both Tuesday and Thursday, and "S" for both Saturday and Sunday. In the 7-day view all 7 bars would be shown, making Tuesday/Thursday and Saturday/Sunday indistinguishable.

Use the 3-letter abbreviation directly instead:

🐛 Proposed fix
-      label: new Date(p.date).toLocaleDateString("en-US", { weekday: "short" }).charAt(0),
+      label: new Date(p.date).toLocaleDateString("en-US", { weekday: "short" }),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
label: new Date(p.date).toLocaleDateString("en-US", { weekday: "short" }).charAt(0),
label: new Date(p.date).toLocaleDateString("en-US", { weekday: "short" }),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/app/components/dashboard/WorkflowActivityChart.tsx` at line 37, The
X-axis label generation in WorkflowActivityChart (the label property in the data
mapping) uses .charAt(0) which collapses distinct weekdays into identical single
letters; update the label expression to use the full 3-letter abbreviation
returned by toLocaleDateString (e.g., new
Date(p.date).toLocaleDateString("en-US", { weekday: "short" })) instead of
slicing to a single character so Tuesday/Thursday and Saturday/Sunday remain
distinguishable.

{/* Peak indicator */}
{peakValue.value > 0 && (
<div className="flex items-center gap-3 mb-3">
<span className="text-xs text-[#5a6350]">CRM</span>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

"CRM" label appears to be stale placeholder text — wrong domain context.

The peak indicator renders "CRM" as a static prefix beside the peak completed count. This is a workflow automation dashboard, not a CRM; the label has no semantic meaning here and will confuse users. Either remove it or replace it with something meaningful (e.g., "Peak").

🐛 Proposed fix
-          <span className="text-xs text-[`#5a6350`]">CRM</span>
+          <span className="text-xs text-[`#5a6350`]">Peak</span>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<span className="text-xs text-[#5a6350]">CRM</span>
<span className="text-xs text-[`#5a6350`]">Peak</span>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/app/components/dashboard/WorkflowActivityChart.tsx` at line 62, The
static "CRM" label in WorkflowActivityChart.tsx is a stale placeholder and
should be removed or replaced with a meaningful label; locate the span rendering
the text (the <span className="text-xs text-[`#5a6350`]">CRM</span> near the peak
completed count) and either delete that span or change its text to a contextual
string like "Peak" (or another suitable term), keeping the existing styling and
layout so only the label content is updated.

@@ -1,4 +1,5 @@
@import "tailwindcss";
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Stylelint import-notation violation: use string notation, not url().

Stylelint requires CSS imports to use the string form.

🔧 Proposed fix
-@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap');
+@import 'https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap');
`@import` 'https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap';
🧰 Tools
🪛 Stylelint (17.9.0)

[error] 2-2: Expected "url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap')" to be "'https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap'" (import-notation)

(import-notation)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ui/src/styles/globals.css` at line 2, Replace the `@import` url(...)
statement in globals.css with the string notation required by Stylelint: change
the current `@import`
url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap');
to the string form `@import`
'https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap';
so the import-notation rule is satisfied.

Comment on lines 126 to +128
@apply bg-background text-foreground;
font-family: 'Inter', system-ui, -apple-system, sans-serif;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Two Stylelint violations on the font-family declaration.

  1. declaration-empty-line-before: a blank line is required before the property when it follows an @apply rule.
  2. font-family-name-quotes: Inter is not a CSS keyword and must be unquoted.
🔧 Proposed fix
 body {
   `@apply` bg-background text-foreground;
-  font-family: 'Inter', system-ui, -apple-system, sans-serif;
+
+  font-family: Inter, system-ui, -apple-system, sans-serif;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@apply bg-background text-foreground;
font-family: 'Inter', system-ui, -apple-system, sans-serif;
}
`@apply` bg-background text-foreground;
font-family: Inter, system-ui, -apple-system, sans-serif;
}
🧰 Tools
🪛 Stylelint (17.9.0)

[error] 127-127: Expected empty line before declaration (declaration-empty-line-before)

(declaration-empty-line-before)


[error] 127-127: Expected no quotes around "Inter" (font-family-name-quotes)

(font-family-name-quotes)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ui/src/styles/globals.css` around lines 126 - 128, The font-family
declaration after the `@apply` rule in the global CSS violates Stylelint: add a
blank line before the font-family property and remove the quotes around Inter
(use Inter without quotes) so the declaration reads an unquoted Inter followed
by the existing fallbacks; update the rule near the `@apply` bg-background
text-foreground block (the font-family line) to satisfy
declaration-empty-line-before and font-family-name-quotes.

Comment on lines +148 to +171
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(12px);
}
to {
opacity: 1;
transform: translateY(0);
}
}

@keyframes shimmer {
0% { background-position: -200% 0; }
100% { background-position: 200% 0; }
}

@keyframes pulse-glow {
0%, 100% { box-shadow: 0 0 20px rgba(186, 242, 102, 0.05); }
50% { box-shadow: 0 0 30px rgba(186, 242, 102, 0.12); }
}

.animate-fade-in-up {
animation: fadeInUp 0.5s ease-out forwards;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

fadeInUp violates the keyframes-name-pattern (kebab-case) rule; update the animation reference too.

The Stylelint keyframes-name-pattern rule requires kebab-case. Renaming the keyframe also requires updating the animation shorthand in the utility class.

🔧 Proposed fix
-@keyframes fadeInUp {
+@keyframes fade-in-up {
   from {
     opacity: 0;
     transform: translateY(12px);
   }
   to {
     opacity: 1;
     transform: translateY(0);
   }
 }

 .animate-fade-in-up {
-  animation: fadeInUp 0.5s ease-out forwards;
+  animation: fade-in-up 0.5s ease-out forwards;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(12px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes shimmer {
0% { background-position: -200% 0; }
100% { background-position: 200% 0; }
}
@keyframes pulse-glow {
0%, 100% { box-shadow: 0 0 20px rgba(186, 242, 102, 0.05); }
50% { box-shadow: 0 0 30px rgba(186, 242, 102, 0.12); }
}
.animate-fade-in-up {
animation: fadeInUp 0.5s ease-out forwards;
}
`@keyframes` fade-in-up {
from {
opacity: 0;
transform: translateY(12px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
`@keyframes` shimmer {
0% { background-position: -200% 0; }
100% { background-position: 200% 0; }
}
`@keyframes` pulse-glow {
0%, 100% { box-shadow: 0 0 20px rgba(186, 242, 102, 0.05); }
50% { box-shadow: 0 0 30px rgba(186, 242, 102, 0.12); }
}
.animate-fade-in-up {
animation: fade-in-up 0.5s ease-out forwards;
}
🧰 Tools
🪛 Stylelint (17.9.0)

[error] 148-148: Expected keyframe name "fadeInUp" to be kebab-case (keyframes-name-pattern)

(keyframes-name-pattern)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ui/src/styles/globals.css` around lines 148 - 171, Rename the
keyframe from fadeInUp to kebab-case (e.g., fade-in-up) and update the animation
reference in the .animate-fade-in-up utility to use the new name (animation:
fade-in-up 0.5s ease-out forwards); ensure the `@keyframes` rule and any other
places referencing fadeInUp are changed to the new kebab-case identifier so it
satisfies the keyframes-name-pattern rule.

@Vamsi-o Vamsi-o merged commit 84bed1f into main May 2, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants